package triFileSystem;

import transaction.Transaction;
import triStorage.IReader;
import triStorage.IWriter;
import triStorage.TriMMPFile;
import utils.*;

//RPageArray doesn't support multi-access
public class RPageArray implements IReader{

	protected Inode inode;
	protected TriMMPFile mmpfile;
	
	protected int pos;
	protected int pageId;
	protected int idx;
	
	public RPageArray(Inode inode, TriMMPFile mmpfile) {
		this.inode = inode;
		this.mmpfile = mmpfile;
		
		idx = 0;
		pageId = inode.getIthPageId(idx);
		pos = Constants.INT_SIZE;
	}
		
	@Override
	public long getReadPos() {
		return pageId * mmpfile.PAGE_SIZE + pos;
	}
	
	@Override
	public void seek(long readPos) {
		pageId = (int)((readPos - 1) / mmpfile.PAGE_SIZE);
		pos = (int)((readPos - 1) % mmpfile.PAGE_SIZE) + 1;
		
		for (idx = 0; ; ++idx) {
			int temp = inode.getIthPageId(idx);
			if (temp < 0 || temp == pageId)
				break;
		}
	
		//System.out.println(">>> seek " + readPos + "(" + pageId + ", " + pos + 
			//	") with index = " + idx + " " + inode.getIthPageId(idx));
			
	}

	private boolean refreshLastPos() {
		++idx;
		pageId = inode.getIthPageId(idx);
		pos = Constants.INT_SIZE;
	
		if (pageId < 0)
			 --idx;
		
		//System.out.println("refreshLastPos " + idx + " " + pageId + " " + pos);
		return pageId >= 0;
	}
	
	@Override
	//if return IO_ERRORCODE then error occurs.
	public Integer readInt() {
		if (pageId < 0) {
			if (!refreshLastPos())
				return null;
		}
		
		int length;
		while (true) {
			length = mmpfile.readInt(pageId, 0);
			if (pos + Constants.INT_SIZE > length) {
				if (!refreshLastPos())
					return null;
			}
			else
				break;
		}
		
		int value = mmpfile.readInt(pageId, pos);
		pos += Constants.INT_SIZE;
		//System.out.println("read on page = " + pageId + " with pos = " + (pos - Constants.INT_SIZE));
		return value;
	}
	
	@Override
	public Long readLong() {
		if (pageId < 0) {
			if (!refreshLastPos())
				return null;
		}
		
		int length;
		while (true) {
			length = mmpfile.readInt(pageId, 0);
			if (pos + Constants.LONG_SIZE > length) {
				if (!refreshLastPos())
					return null;
			}
			else
				break;
		}
		
		long value = mmpfile.readLong(pageId, pos);
		pos += Constants.LONG_SIZE;
		//System.out.println("read on page = " + pageId + " with pos = " + (pos - Constants.LONG_SIZE));
		return value;
	}
	
	@Override	
	public String readString() {
		
		Integer strLen = readInt();
		if (strLen == null || strLen < 0)
			return null;
		else if (strLen == 0)
			return "";
		
		byte[] bytes = new byte[strLen];
		int length;
		//start position
		for (int i = 0, count; i < strLen; ) {
			length = mmpfile.readInt(pageId, 0);
			if (length - pos <= strLen - i)
				count = length - pos;
			else
				count = strLen - i;
			mmpfile.readBytes(pageId, pos, bytes, i, count);
			i += count;
			pos += count;
			if (i < strLen) {
				if (!refreshLastPos())
					return null;
			}
		}
			
		return Converter.Bytes2String(bytes, 0);
	}
	
	@Override
	public void skipString() {
		Integer strLen = readInt();
		if (strLen == null || strLen <= 0)
			return;
		
		//start position
		int length;
		for (int i = 0, count; i < strLen; ) {
			length = mmpfile.readInt(pageId, 0);
			if (length - pos <= strLen - i)
				count = length - pos;
			else
				count = strLen - i;
			i += count;
			pos += count;
			if (i < strLen) {
				if (!refreshLastPos())
					return;
			}
		}
		
	}
	 
	@Override
	public IWriter toWriter(Transaction tx) {
		return new WPageArray(inode, mmpfile, tx);
	}
	
	@Override
	public void dispose() {
		//do nothing
	}


	
}
